/**
 *  @file StackT.h
 */
#ifndef _STACKT_H_
#define _STACKT_H_

#include "../common/CppMemPool.h"
#include "ListT.h"
#include "../threading/SyncLock.h"
#include "../threading/SyncWrap.h"

namespace cppflib
{

namespace collections
{

/**
 *  A Last-In-First-Out stack for primitive types (int, char, ...)
 */
template <class TYPE>
class StackT : public BaseObject
{
private:
   ListT<TYPE> * pList;
   threading::SyncLock *pLock;

   StackT(void)
   {
      pList = NULL;
      pLock = NULL;
   }
public:
   /**
    *  @param [in] isThreadSafe -- true to make this stack thread safe
    */
   explicit StackT(bool isThreadSafe)
   {
      this->pList = new ListT<TYPE>();
      cppmempool::Retain(this->pList);

      if (isThreadSafe) {
         pLock = new threading::SyncLock();
         cppmempool::Retain(this->pLock);
      }
      else {
         pLock = NULL;
      }
   }

   virtual ~StackT()
   {
      Clear();
      cppmempool::Release(pList);
      if (pLock)
         cppmempool::Release(pLock);
   }

   /**
    *  Add data to top of stack
    */
   void Push(TYPE data)
   {
      threading::SyncWrap<threading::SyncLock> wrap(pLock);
      pList->AddHead(data);
   }

   /** 
    *  Pop from top of stack (throws exception if stack is empty)
    */
   TYPE Pop(void)
   {
      threading::SyncWrap<threading::SyncLock> wrap(pLock);
      TYPE data = pList->RemoveAt(0);
      return data;
   }

   /** 
    *  Take a look on top of stack without removing (throws exception if stack is empty)
    */
   TYPE Peek(void)
   {
      threading::SyncWrap<threading::SyncLock> wrap(pLock);
      return pList->Get(0);
   }

   /** 
    *  Get number of elements
    */
   u32_t GetCount(void)
   {
      threading::SyncWrap<threading::SyncLock> wrap(pLock);
      return pList->GetCount();
   }

   /** 
    *  Test whether an element exists
    */
   bool Contains(TYPE data)
   {
      threading::SyncWrap<threading::SyncLock> wrap(pLock);
      for (int i = 0 ; i < static_cast<int>(pList->GetCount()); ++i)
         if (data == pList->Get(i))
            return true;

      return false;
   }

   /**
    *  Clear whole stack
    */
   void Clear(void)
   {
      threading::SyncWrap<threading::SyncLock> wrap(pLock);
      pList->Clear();
   }

};


/**
 *  Stack for BaseObject derived classes
 */
template <class TYPE>
class StackCppObjT : public BaseObject
{
private:
   ListCppObjT<TYPE> * pList;
   threading::SyncLock *pLock;

   StackCppObjT(void)
   {
      pList = NULL;
      pLock = NULL;
   }

public:

   /**
    *  @param [in] isThreadSafe -- true to make this stack thread safe
    */
   explicit StackCppObjT(bool isThreadSafe)
   {
      this->pList = new ListCppObjT<TYPE>();
      cppmempool::Retain(this->pList);

      if (isThreadSafe) {
         pLock = new threading::SyncLock();
         cppmempool::Retain(this->pLock);
      }
      else {
         pLock = NULL;
      }
   }

   virtual ~StackCppObjT()
   {
      Clear();
      cppmempool::Release(pList);
      if (pLock)
         cppmempool::Release(pLock);
   }

   /**
    *  Add data to top of stack
    */
   void Push(TYPE *data)
   {
      threading::SyncWrap<threading::SyncLock> wrap(pLock);
      if (data)
         pList->AddHead(data);
   }

   /** 
    *  Pop data from top of stack (throws exception if stack is empty)
    *
    *  @note caller should release data returned
    */
   TYPE *Pop(void)
   {
      threading::SyncWrap<threading::SyncLock> wrap(pLock);
      TYPE* data = pList->Get(0);
      cppmempool::Retain(data); // retain first so that RemoveAt will only delete the slot but not data itself
      pList->RemoveAt(0);
      
      return data;
   }

   /**
    *  Pop data from top of stack and don't return the object popped
    *  @return number of element left after poping
    */
   u32_t PopAndRemove(void)
   {
      threading::SyncWrap<threading::SyncLock> wrap(pLock);
      pList->RemoveAt(0);
      return pList->GetCount();
   }

   /** 
    *  Take a look on top of stack without removing (throws exception if stack is empty)
    */
   TYPE *Peek(void)
   {
      threading::SyncWrap<threading::SyncLock> wrap(pLock);
      return pList->Get(0);
   }

   /** 
    *  Get number of elements
    */
   u32_t GetCount(void)
   {
      threading::SyncWrap<threading::SyncLock> wrap(pLock);
      return pList->GetCount();
   }

   /** 
    *  Test whether an element exists
    */
   bool Contains(const TYPE &data)
   {
      threading::SyncWrap<threading::SyncLock> wrap(pLock);

      for (int i = 0 ; i < static_cast<int>(pList->GetCount()); ++i)
         if (data.Equals(*(pList->Get(i))))
            return true;
     
      return false;
   }

   /**
    *  Clear whole stack
    */
   void Clear(void)
   {
      threading::SyncWrap<threading::SyncLock> wrap(pLock);
      pList->Clear();
   }


};

/**
 *  Declare some stack factory type
 */
typedef StackT<char> StackChar;
typedef StackT<u8_t> StackByte;
typedef StackT<wc_t> StackWChar;
typedef StackT<short> StackShort;
typedef StackT<u16_t> StackUShort;
typedef StackT<int> StackInt;
typedef StackT<u32_t> StackUInt;
typedef StackT<long> StackLong;
typedef StackT<unsigned long> StackULong;
typedef StackT<i64_t> StackInt64;
typedef StackT<u64_t> StackUInt64;
typedef StackT<bool> StackBool;
typedef StackT<float> StackFloat;
typedef StackT<double> StackDouble;
typedef StackT<ptr_t> StackPtr;



} // end of namespace collections


} // end of namespace cppflib



#endif
